package pacBiopackage;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.StringTokenizer;

import tools.fasta.FastaSeq;

public class PacBioCorrectionByAlignmentConsenus implements CorretionbyAlignment{

	FastaSeq fs= null;
	PacBioSNPLineReader alignmentReader = null;
	private int frame_counter =0;
	private List<String> row = null;
	private long prevBufferReaderposition = 0;
	private List<Integer> notCorrected = null;
	private PacBioErrorStatisticsContainer stat = null;

	public PacBioCorrectionByAlignmentConsenus(PacBioSNPLineReader pbrs,PacBioErrorStatisticsContainer stat)
	{
		this.alignmentReader = pbrs;
		this.stat=stat;
	}
//	private StringBuffer mismatch(StringBuffer sequence, int pos)
//	{
//		sequence.append(row.get(4));
//		updateStatistic(2);
//		stat.getErrors().put("mis", stat.getErrors().get("mis")+1);
//		stat.getMismatchErrorType().get(row.get(3)).put(row.get(4), stat.getMismatchErrorType().get(row.get(3)).get(row.get(4))+1);
//		stat.getPositionsErrors2().put(pos,stat.getPositionsErrors2().get(pos)+1);
//		return sequence;
//		
//	}
	
	private StringBuffer deletionMismatch(StringBuffer sequence,int pos)
	{
		if(row.get(4).equals("-"))
		{
			//deletion
			updateStatistic(1);
			stat.getErrors().put("del",stat.getErrors().get("del")+1);
			stat.getPositionsErrors2().put(pos,stat.getPositionsErrors2().get(pos)+1);
			--frame_counter;
		}
		else
		{
			sequence.append(row.get(4));
			updateStatistic(2);
			stat.getErrors().put("mis", stat.getErrors().get("mis")+1);
			stat.getMismatchErrorType().get(row.get(3)).put(row.get(4), stat.getMismatchErrorType().get(row.get(3)).get(row.get(4))+1);
			stat.getPositionsErrors2().put(pos,stat.getPositionsErrors2().get(pos)+1);
		}
		return sequence;
	}
	
	private void updateStatistic(int mode)
	{
		StringBuilder sb = new StringBuilder();
		int tempBeginIndex = Integer.valueOf(row.get(2))-5;
		int tempEndIndex = Integer.valueOf(row.get(2))+5;
		if(tempBeginIndex<0)tempBeginIndex=0;
		if(tempEndIndex>this.fs.getSeq().length())tempEndIndex=this.fs.getSeq().length();
		sb.append(this.fs.getSeq().substring(tempBeginIndex, Integer.valueOf(row.get(2))));
		sb.append(this.fs.getSeq().substring(Integer.valueOf(row.get(2)),tempEndIndex));
		
		if(mode==0)
		{		
			updateMap(stat.getInsertEnviourment(),sb.toString());
		}
		else if(mode==1)
		{
			updateMap(stat.getDeletionEnviourment(), sb.toString());
		}
		else if(mode==2)
		{
			updateMap(stat.getSNPEnviourment(), sb.toString());
		}
	}
	
	private void updateMap(HashMap<String,Integer> map,String sb)
	{
		if(map.containsKey(sb))
		{
			map.put(sb, map.get(sb)+1);
		}
		else
		{
			map.put(sb,1);
		}
	}
	
//	private StringBuffer deletion(StringBuffer tempSequence, int pos)
//	{
//		//deletion
//		updateStatistic(1);
//		for(int j=0; j < Integer.valueOf(row.get(3));++j)
//		{
//			stat.getErrors().put("del",stat.getErrors().get("del")+1);
//			--frame_counter;
//			stat.getPositionsErrors2().put(pos+j,stat.getPositionsErrors2().get(pos+j)+1);
//		}		
//		return tempSequence;
//	}
//	
	
	private StringBuffer insert(StringBuffer tempSequence,int position)
	{
		stat.getPositionsErrors2().put(position, stat.getPositionsErrors2().get(position)+row.get(4).length());
		stat.getErrors().put("ins",stat.getErrors().get("ins")+row.get(4).length());
		//first construct the string
		updateStatistic(0);
		//stat.getInsertEnviourment().
		for(int j=0; j < row.get(4).length();++j)
		{
			if(row.get(4).charAt(j) != 'N')
			{
				stat.getInsertFreq().put(String.valueOf(row.get(4).charAt(j)), stat.getInsertFreq().get(String.valueOf(row.get(4).charAt(j)))+1);
			}
			tempSequence.append(row.get(4).charAt(j));
			++frame_counter;
		}
		return tempSequence;		
	}
	
	
	private boolean setFilePointer(String id) throws IOException
	{
		//first check if we already have seen the id
		long filePosition  = 0;
		boolean flag=false;
		if(prevBufferReaderposition!=0)
		{
			filePosition = prevBufferReaderposition;
			alignmentReader.setFilePosition(prevBufferReaderposition);
		}
		else
		{
			filePosition=alignmentReader.getFilePosition();
		}			
		if(alignmentReader.ready())
		{
			List<String> tempRow = alignmentReader.next();
			if(tempRow.get(1).equals(id))
			{
				row = tempRow;
				flag=true;
			}
			else
			{
				//now we have to search until we find the correct file pointer position
				int identifier = Integer.valueOf(id.substring(4));
				while(alignmentReader.ready())
				{
					filePosition=alignmentReader.getFilePosition();
					tempRow = alignmentReader.next();
					if(tempRow.get(1).equals(id))
					{
						alignmentReader.setFilePosition(filePosition);
						row = tempRow;
						flag=true;
						break;
					}
					else if(Integer.valueOf(tempRow.get(1).substring(4))> identifier)
					{
						flag=false;
						break;//TODO error catching!!
					}
				}
			}
		}
		if(flag)
		{
			prevBufferReaderposition = alignmentReader.getFilePosition();
		}
		return flag;
	}
//	private StringBuffer checkAfterDeletion(StringBuffer tempSequence,int pos)
//	{		
//		try
//		{
//			List<String> tempSaveRow = row;
//			int counter=0;
//			while(alignmentReader.ready())
//			{ 
//				++counter;
//				prevBufferReaderposition = alignmentReader.getFilePosition();
//				row = alignmentReader.next();
//				if(!row.get(1).equals(tempSaveRow.get(1))||!row.get(2).equals(tempSaveRow.get(2)))
//				{
//					//alignmentReader.setFilePosition(prevBufferReaderposition);
////					if(counter==1)
////					{
////						notCorrected.add(tempSequence.length());
////						stat.getPositionsErrors().put(tempSequence.length(), stat.getPositionsErrors().get(tempSequence.length())+1); //do i have to consider the frameshift? or just refere to the orignal string?... -> would perefere the orginal string.... than i have to change it! TODO
////						tempSequence.append(fs.getSeq().charAt(pos));
////					}
//					break;
//				}
//				
//				else
//				{
//					//check if it is either var or ref
//					if(row.get(0).equals("ref"))
//					{
//						tempSequence.append(row.get(3));
//					}
//					else if(row.get(0).equals("var"))
//					{
//						tempSequence = mismatch(tempSequence,pos);
//					}
//					else if(row.get(0).equals("del"))
//					{
//						int tempSequenclength=tempSequence.length();
//						tempSequence = deletion(tempSequence,pos);
//						tempSequence=checkAfterDeletion(tempSequence,pos+1+tempSequence.length()-tempSequenclength);						
//					}
//					else if(row.get(0).equals("insert"))
//					{
//						int tempSequenclength=tempSequence.length();
//						tempSequence=insert(tempSequence,pos);
//						tempSequence=checkAfterInseration(tempSequence,pos+tempSequence.length()-tempSequenclength);
//					}
//				}
//			}	
//		}
//		catch(Exception e)
//		{
//			e.printStackTrace();
//		}
//		return tempSequence;		
//	}
	
	
	private StringBuffer checkAfterInseration(StringBuffer tempSequence,int pos)
	{		
		try
		{
			List<String> tempSaveRow = row;
			while(alignmentReader.ready())
			{
				prevBufferReaderposition = alignmentReader.getFilePosition();
				row = alignmentReader.next();
				if(!row.get(1).equals(tempSaveRow.get(1))||!row.get(2).equals(tempSaveRow.get(2)))
				{
					//alignmentReader.setFilePosition(prevBufferReaderposition);
					break;
				}
				
				else
				{
					//check if it is either var or ref
					if(row.get(0).equals("ref"))
					{
						tempSequence.append(row.get(3));
					}
					else if(row.get(0).equals("var"))
					{
						tempSequence = deletionMismatch(tempSequence,pos);
					}
					else if(row.get(0).equals("insert"))
					{
						int tempSequenclength=tempSequence.length();
						tempSequence=insert(tempSequence,pos);
						tempSequence=checkAfterInseration(tempSequence,pos+tempSequence.length()-tempSequenclength);
					}
					/*
					else if(row.get(0).equals("del"))
					{
						int tempSequenclength=tempSequence.length();
						tempSequence = deletion(tempSequence,pos);
						tempSequence=checkAfterDeletion(tempSequence,pos+1+tempSequence.length()-tempSequenclength);						
					}
					*/
				}
			}	
		}
		catch(Exception e)
		{
			e.printStackTrace();
		}
		return tempSequence;		
	}
	
	private StringBuffer fillUnknown()
	{
		StringBuffer tempSequence = new StringBuffer(fs.getSeq());
		notCorrected = new ArrayList<Integer>();
		for(int i=0; i < fs.getSeq().length();++i)
		{
			notCorrected.add(i);
		}
		return tempSequence;
	}
	
	
	private StringBuffer correctIt(int counter)
	{
		//now we have to go until we find an entry which is not part of this read.
		
		StringBuffer tempSequence = new StringBuffer();
		String header = row.get(1);
		notCorrected = new ArrayList<Integer>();
		int i=0;
		boolean out=false;
		try
		{
			while(!out && alignmentReader.ready())
			{			
				if(i+1 <Integer.valueOf(row.get(2)) && i < fs.getSeq().length())
				{
					notCorrected.add(tempSequence.length());
					stat.getPositionsErrors().put(tempSequence.length(), stat.getPositionsErrors().get(tempSequence.length())+1); //do i have to consider the frameshift? or just refere to the orignal string?... -> would perefere the orginal string.... than i have to change it! TODO
					tempSequence.append(fs.getSeq().charAt(i));
					++i;
					continue;
				}
				if(i+1==Integer.valueOf(row.get(2)))
				{
					if(row.get(0).equals("ins"))
					{
						//first how many insert we have to do and what about the frame shift?
						int tempSequenclength=tempSequence.length();
						tempSequence=insert(tempSequence,i+1);
						tempSequence=checkAfterInseration(tempSequence,i+1+tempSequence.length()-tempSequenclength);
						//now we have to check if the next entry is ref or not.
						//row = alignmentReader.next();
						//tempSequence.append(row.g)
						
					}
					else
					{
						if(row.get(0).equals("ref"))
						{
							tempSequence.append(row.get(3));
						}
						
						else if(row.get(0).equals("var"))
						{
							//check if we have a wrong base or even a deletion
							tempSequence = deletionMismatch(tempSequence,i+1);
						}
						try
						{
							prevBufferReaderposition = alignmentReader.getFilePosition();
							if(alignmentReader.ready())
							{
								row = alignmentReader.next();
							}
							else
							{
								out=true;
							}
						}
						catch(Exception e)
						{
							e.printStackTrace();
						}
					}
					try
					{
						if(!row.get(1).equals(header) && !out)
						{
							out=true;
						}
					}
					catch(Exception e)
					{
						e.printStackTrace();
					}
					++i;
				}		
			}
		}
		catch(IOException e)
		{
			e.printStackTrace();
		}
		while(i<fs.getSeq().length())
		{
			notCorrected.add(i+frame_counter);
			tempSequence.append(fs.getSeq().charAt(i));
			++i;
		}
		return tempSequence;
	}
	
	public StringBuffer correctByAlignment(FastaSeq fs,int counter)
	{
		//first find the correct identifier in the alignmentReader stream
		this.fs=fs;
		this.frame_counter=0;
		String header = null;
		//StringTokenizer st = new StringTokenizer(fs.getHeaderName()," ");
		StringTokenizer st = new StringTokenizer(fs.getQname()," ");
		header = st.nextToken();
		st=null;
		StringBuffer corrected_sequence = null;
		try
		{
			boolean found = setFilePointer(header);
			//now we have to correct the read
			if(found)
			{
				corrected_sequence = correctIt(counter);
			}
			else
			{
				corrected_sequence=fillUnknown();
			}
		}
		catch(Exception e)
		{
			e.printStackTrace();
		}		
		return corrected_sequence;
	}
	
	
	public List<Integer> getPositions()
	{
		return this.notCorrected;
	}
	
	//missing error container for the alignment
	//connection with kmers
	//test!
	//correct function for calling
}



